﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Osteox.ManagedCv;
using Osteox.ManagedCv.WinForm;

namespace WinFormSample
{
    public partial class MainForm : Form
    {
        private const int MaxUndoSteps = 12;
        private ImageViewer _ImageViewer = new ImageViewer();
        private LinkedList<ManagedImage> _ImagesCollection = new LinkedList<ManagedImage>();
        private LinkedListNode<ManagedImage> _CurrentImageNode = null;
        private SampleProperty _FilterProperty = SampleProperty.Current;

        public MainForm()
        {
            InitializeComponent();
            _ImageViewer.SizeMode = PictureBoxSizeMode.Zoom;
            _ImageViewer.Left = 0;
            _ImageViewer.Top = 0;
            _ImageViewer.Dock = DockStyle.Fill;
            panelContainer.Controls.Add(_ImageViewer);
            updateNodeIndicator();
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        void PushImage(ManagedImage image)
        {
            LinkedListNode<ManagedImage> node = _ImagesCollection.Find(this.CurrentImage);
            if (node != null)
            {
                node = node.Next;
            }
            while (node != null)
            {
                LinkedListNode<ManagedImage> nextNode = node.Next;
                _ImagesCollection.Remove(node);
                node = nextNode;
            }
            _ImagesCollection.AddLast(image);
            if (_ImagesCollection.Count > MaxUndoSteps && _ImagesCollection.Count > 0)
            {
                _ImagesCollection.RemoveFirst();
            }
            _CurrentImageNode = _ImagesCollection.Last;
            _ImageViewer.Image = this.CurrentImage;
            updateNodeIndicator();
        }

        void updateNodeIndicator()
        {
            int index = 0;
            LinkedListNode<ManagedImage> node = _ImagesCollection.First;
            while (node != _CurrentImageNode && node != null)
            {
                index++;
                node = node.Next;
            }
            toolStripStatusLabelImage.Text = string.Format("{0} / {1}", _ImagesCollection.Count == 0 ? 0 : index + 1, _ImagesCollection.Count);

            ManagedImage image = this.CurrentImage;
            toolStripStatusLabel.Text = _CurrentImageNode == null ? "Welcome ManagedCv samples" :
                string.Format("Width={0}, Height={1}, Depth={2}, Channels={3}", image.Width, image.Height, image.Depth, image.Channels);
        }

        ManagedImage CurrentImage
        {
            get { return _CurrentImageNode == null ? null : _CurrentImageNode.Value; }
        }

        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            OpenFileDialog dialog = new OpenFileDialog();
            dialog.Filter = "Image Files|*.bmp;*.png;*.jpg;*.jpeg;*.tif;*.tiff|All Files|*.*";
            if (dialog.ShowDialog(this) == DialogResult.OK)
            {
                PushImage(ManagedCvInvoke.LoadImageW(dialog.FileName));
            }
        }

        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SaveFileDialog dialog = new SaveFileDialog();
            dialog.DefaultExt = "png";
            dialog.Filter = "Image Files|*.bmp;*.png;*.jpg|All Files|*.*";
            if (dialog.ShowDialog(this) == DialogResult.OK)
            {
                ManagedImage source = CurrentImage;
                if (source != null)
                {
                    ManagedCvInvoke.SaveImage(dialog.FileName, source);
                }
            }
        }

        private void undoToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (_CurrentImageNode.Previous != null)
            {
                _CurrentImageNode = _CurrentImageNode.Previous;
                _ImageViewer.Image = this.CurrentImage;
                updateNodeIndicator();
            }
        }

        private void redoToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (_CurrentImageNode.Next != null)
            {
                _CurrentImageNode = _CurrentImageNode.Next;
                _ImageViewer.Image = this.CurrentImage;
                updateNodeIndicator();
            }
        }

        private void smoothToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SmoothFilterProperty property = _FilterProperty.GetProperty<SmoothFilterProperty>();
            FilterPropertyDialog dialog = new FilterPropertyDialog("Smooth", property);
            if (dialog.ShowDialog(this) != DialogResult.OK) return;

            ManagedImage source = CurrentImage;
            if (source != null)
            {
                ManagedImage target = ManagedCvInvoke.CloneImage(source);
                ManagedCvInvoke.Smooth(source, target, property.Type, property.Size1, property.Size2, property.Sigma1, property.Sigma2);
                PushImage(target);
            }
        }

        private void sobelToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SobelFilterProperty property = _FilterProperty.GetProperty<SobelFilterProperty>();
            FilterPropertyDialog dialog = new FilterPropertyDialog("Sobel", property);
            if (dialog.ShowDialog(this) != DialogResult.OK) return;

            ManagedImage source = CurrentImage;
            if (source != null)
            {
                ManagedImage target = new ManagedImage(new Size2D32s(source.Width, source.Height), PixelDepth.Depth16S, source.Channels);
                ManagedCvInvoke.Sobel(source, target, property.XOrder, property.YOrder, property.ApertureSize);
                ManagedImage display = new ManagedImage(new Size2D32s(source.Width, source.Height), PixelDepth.Depth8U, source.Channels);
                ManagedCvInvoke.ConvertScaleAbs(target, display);
                PushImage(display);
            }
        }

        private void cannyToolStripMenuItem_Click(object sender, EventArgs e)
        {
            CannyFilterProperty property = _FilterProperty.GetProperty<CannyFilterProperty>();
            FilterPropertyDialog dialog = new FilterPropertyDialog("Canny", property);
            if (dialog.ShowDialog(this) != DialogResult.OK) return;

            ManagedImage source = CurrentImage;
            if (source != null)
            {
                ManagedImage target = ManagedCvInvoke.CloneImage(source);
                ManagedCvInvoke.Canny(source, target, property.Threshold1, property.Threshold2, property.ApertureSize);
                PushImage(target);
            }
        }

        private void bgrGrayToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ManagedImage source = CurrentImage;
            if (source != null)
            {
                ManagedImage target = new ManagedImage(new Size2D32s(source.Width, source.Height), source.Depth, 1);
                ManagedCvInvoke.CvtColor(source, target, ColorMapType.BGR2GRAY);
                PushImage(target);
            }
        }

        private void grayBgrToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ManagedImage source = CurrentImage;
            if (source != null)
            {
                ManagedImage target = new ManagedImage(new Size2D32s(source.Width, source.Height), source.Depth, 3);
                ManagedCvInvoke.CvtColor(source, target, ColorMapType.GRAY2BGR);
                PushImage(target);
            }
        }

        private void laplaceToolStripMenuItem_Click(object sender, EventArgs e)
        {
            LaplaceFilterProperty property = _FilterProperty.GetProperty<LaplaceFilterProperty>();
            FilterPropertyDialog dialog = new FilterPropertyDialog("Laplace", property);
            if (dialog.ShowDialog(this) != DialogResult.OK) return;

            ManagedImage source = CurrentImage;
            if (source != null)
            {
                ManagedImage target = new ManagedImage(new Size2D32s(source.Width, source.Height), PixelDepth.Depth16S, source.Channels);
                ManagedCvInvoke.Laplace(source, target, property.ApertureSize);
                ManagedImage display = new ManagedImage(new Size2D32s(source.Width, source.Height), PixelDepth.Depth8U, source.Channels);
                ManagedCvInvoke.ConvertScaleAbs(target, display);
                PushImage(display);
            }
        }

        private void erodeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            KernelFilterProperty property = _FilterProperty.GetProperty<KernelFilterProperty>();
            FilterPropertyDialog dialog = new FilterPropertyDialog("Erode", property);
            if (dialog.ShowDialog(this) != DialogResult.OK) return;

            ManagedImage source = CurrentImage;
            if (source != null)
            {
                ManagedImage target = ManagedCvInvoke.CloneImage(source);
                ManagedConvKernel kernel = new ManagedConvKernel(property.Cols, property.Rows, property.AnchorX, property.AnchorY, property.Shape);
                ManagedCvInvoke.Erode(source, target, kernel);
                PushImage(target);
            }
        }

        private void dilateToolStripMenuItem_Click(object sender, EventArgs e)
        {
            KernelFilterProperty property = _FilterProperty.GetProperty<KernelFilterProperty>();
            FilterPropertyDialog dialog = new FilterPropertyDialog("Dilate", property);
            if (dialog.ShowDialog(this) != DialogResult.OK) return;

            ManagedImage source = CurrentImage;
            if (source != null)
            {
                ManagedImage target = ManagedCvInvoke.CloneImage(source);
                ManagedConvKernel kernel = new ManagedConvKernel(property.Cols, property.Rows, property.AnchorX, property.AnchorY, property.Shape);
                ManagedCvInvoke.Dilate(source, target, kernel);
                PushImage(target);
            }
        }

        private void morphologyToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MorphologyExFilterProperty property = _FilterProperty.GetProperty<MorphologyExFilterProperty>();
            FilterPropertyDialog dialog = new FilterPropertyDialog("Morphology", property);
            if (dialog.ShowDialog(this) != DialogResult.OK) return;

            ManagedImage source = CurrentImage;
            if (source != null)
            {
                ManagedImage target = ManagedCvInvoke.CloneImage(source);
                ManagedConvKernel kernel = new ManagedConvKernel(property.Cols, property.Rows, property.AnchorX, property.AnchorY, property.Shape);
                ManagedCvInvoke.MorphologyEx(source, target, ManagedImage.Empty, kernel, property.Method, property.Iterations);
                PushImage(target);
            }
        }

        private void sharpenToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ManagedImage source = CurrentImage;
            if (source != null)
            {
                ManagedImage target = ManagedCvInvoke.CloneImage(source);
                ManagedMat kernel = new ManagedMat(3, 3, MatFlag.CV32SC1);
                ManagedArrElementAccessor32S accessor = new ManagedArrElementAccessor32S(kernel);
                accessor[0, 0, 0] = +0;
                accessor[0, 1, 0] = -1;
                accessor[0, 2, 0] = +0;
                accessor[1, 0, 0] = -1;
                accessor[1, 1, 0] = +5;
                accessor[1, 2, 0] = -1;
                accessor[2, 0, 0] = +0;
                accessor[2, 1, 0] = -1;
                accessor[2, 2, 0] = +0;
                ManagedCvInvoke.Filter2D(source, target, kernel);
                PushImage(target);
            }
        }

        private void adaptiveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AdaptiveThresholdFilterProperty property = _FilterProperty.GetProperty<AdaptiveThresholdFilterProperty>();
            FilterPropertyDialog dialog = new FilterPropertyDialog("Adaptive Threshold", property);
            if (dialog.ShowDialog(this) != DialogResult.OK) return;

            ManagedImage source = CurrentImage;
            if (source != null)
            {
                ManagedImage target = ManagedCvInvoke.CloneImage(source);
                ManagedCvInvoke.AdaptiveThreshold(source, target, property.MaxValue, property.AdaptiveMethod, property.ThresholdType, property.BlockSize, property.Param1);
                PushImage(target);
            }
        }

        private void thresholdToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ThresholdFilterProperty property = _FilterProperty.GetProperty<ThresholdFilterProperty>();
            FilterPropertyDialog dialog = new FilterPropertyDialog("Threshold", property);
            if (dialog.ShowDialog(this) != DialogResult.OK) return;

            ManagedImage source = CurrentImage;
            if (source != null)
            {
                ManagedImage target = ManagedCvInvoke.CloneImage(source);
                ManagedCvInvoke.Threshold(source, target, property.Threshold, property.MaxValue, property.ThresholdType);
                PushImage(target);
            }
        }

        private void mergeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            BrowseImagesDialog browseDialog = new BrowseImagesDialog(_ImagesCollection);
            if (browseDialog.ShowDialog() == DialogResult.OK)
            {
                List<ManagedImage> selected = new List<ManagedImage>(browseDialog.Selected);
                if (selected.Count > 0)
                {
                    ManagedImage source = selected[0];
                    if (source != null)
                    {
                        ManagedImage target = new ManagedImage(new Size2D32s(source.Width, source.Height), source.Depth, source.Channels);
                        ManagedCvInvoke.Zero(target);
                        double weight = 1.0 / selected.Count;
                        for (int i = 0; i < selected.Count; i++)
                        {
                            ManagedCvInvoke.AddWeighted(selected[i], weight, target, 1.0, 0, target);
                        }
                        PushImage(target);
                    }
                }
            }
        }

        private void toolStripStatusLabelImage_Click(object sender, EventArgs e)
        {
            BrowseImagesDialog browseDialog = new BrowseImagesDialog(_ImagesCollection);
            if (browseDialog.ShowDialog() == DialogResult.OK)
            {
            }
        }

        private void maxToolStripMenuItem_Click(object sender, EventArgs e)
        {
            BrowseImagesDialog browseDialog = new BrowseImagesDialog(_ImagesCollection);
            if (browseDialog.ShowDialog() == DialogResult.OK)
            {
                List<ManagedImage> selected = new List<ManagedImage>(browseDialog.Selected);
                if (selected.Count > 0)
                {
                    ManagedImage source = selected[0];
                    if (source != null)
                    {
                        ManagedImage target = new ManagedImage(new Size2D32s(source.Width, source.Height), source.Depth, source.Channels);
                        ManagedCvInvoke.Zero(target);
                        for (int i = 0; i < selected.Count; i++)
                        {
                            ManagedCvInvoke.Max(selected[i], target, target);
                        }
                        PushImage(target);
                    }
                }
            }
        }

        private void inverseToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ManagedImage source = CurrentImage;
            if (source != null)
            {
                ManagedImage target = ManagedCvInvoke.CloneImage(source);
                ManagedCvInvoke.Scale(source, target, -1, 255);
                PushImage(target);
            }
        }

        private void equalizeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ManagedImage source = CurrentImage;
            if (source != null)
            {
                ManagedImage target = ManagedCvInvoke.CloneImage(source);
                ManagedCvInvoke.EqualizeHist(source, target);
                PushImage(target);
            }
        }

        private void normalizeToolStripMenuItem_Click(object sender, EventArgs e)
        {               //ManagedImage source = CurrentImage;
            //if (source != null)
            //{
            //    ManagedImage target = ManagedCvInvoke.CloneImage(source);
            //    ManagedCvInvoke.NormalizeHist(source, target);
            //    PushImage(target);
            //}
        }

        private void distanceToolStripMenuItem_Click(object sender, EventArgs e)
        {
            DistanceFilterProperty property = _FilterProperty.GetProperty<DistanceFilterProperty>();
            FilterPropertyDialog dialog = new FilterPropertyDialog("Distance transform", property);
            if (dialog.ShowDialog(this) != DialogResult.OK) return;

            ManagedImage source = CurrentImage;
            if (source != null)
            {
                ManagedImage target = new ManagedImage(new Size2D32s(source.Width, source.Height), PixelDepth.Depth32F, 1);
                ManagedCvInvoke.DistTransform(source, target, property.DistanceType, property.MaskSize);
                double min, max;
                ManagedCvInvoke.MinMaxLoc(target, out min, out max);
                if (max > min)
                {
                    ManagedImage display = new ManagedImage(new Size2D32s(source.Width, source.Height), PixelDepth.Depth8U, 1);
                    ManagedCvInvoke.Scale(target, target, 1, -min);
                    ManagedCvInvoke.Scale(target, display, 255 / (max - min));
                    PushImage(display);
                }
            }
        }

        private void scaleToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ScaleFilterProperty property = _FilterProperty.GetProperty<ScaleFilterProperty>();
            FilterPropertyDialog dialog = new FilterPropertyDialog("Scale", property);
            if (dialog.ShowDialog(this) != DialogResult.OK) return;

            ManagedImage source = CurrentImage;
            if (source != null)
            {
                ManagedImage target = ManagedCvInvoke.CloneImage(source);
                ManagedCvInvoke.Scale(source, target, property.Scale, property.Shift);
                PushImage(target);
            }
        }
    }
}
